package chatNI;



import java.io.File;
import java.io.IOException;

import java.net.*;
import java.util.Observable;
import java.util.Observer;

import chatController.ChatController;
import chatModel.*;
import chatModel.ChatModel.Val;


import messages.*;


/* 
 * @author Yahya-Albaba
*/

public class ChatNI implements Observer {

	/**
	 * @param  DEST_PORT socket number of the remote user for this application
	 * @param  LISTEN_PORT the socket number that we use to listen on the application
	 *			for the local user
	 * @param  BROADCAST the broadcast address which is equal to 255.255.255.255 
	 * @param  rcvThread the thread that is use to listen/receive
	 * @param  cm the chat model used for this instance of the chat system
	 * @param  cc the chat controller used for this instance of the chat controller
	*/
	
	protected static final int DEST_PORT = 21000;
    protected static final int LISTEN_PORT = 21000; 
    protected static final String BROADCAST = "255.255.255.255";
    private ThreadReceiveMsg rcvThread;
    private ChatModel cm;
    private ChatController cc;
    
    public ChatNI(ChatModel cm ) {
    	this.cm=cm;
   
    	System.out.println("on doit commencer le thread d'ecoute ici");
    }

    /**
     * This method allows us to start the listening thread when the user gets connected
     */   
    
    public void startThread(){
    	rcvThread = new ThreadReceiveMsg(this, LISTEN_PORT);
    	rcvThread.start();
    }
    
    
    /**
     * This method allows us to set current chat controller for this instance of the chat system 
     * @param  current chatController
     */
	public void setController(ChatController chatController) {
		this.cc = chatController;
	}


	/**
	 * sendHello allows us to send a hello in bradcast mode to all of the machines in my network
	 *
	 * @param  hostname  the host name of the machine
	 * @param  ack the ack consists of specifying if the hello is sent to notify other
	 * 				users who are connected if it is a hello to establish a connection
	 * 				in that case the ack is true. Or the ack is false, and that means that
	 * 				we are answering a hello that will permit that new user to know who 
	 * 				is the local user
	 */

	public void sendHello(String hostname, boolean ack) {
		// TODO Auto-generated method stub
		try{
		
			ThreadSendMsg msgThread=new ThreadSendMsg(MessagesFactory.getHello(hostname, ack), InetAddress.getByName("255.255.255.255"), DEST_PORT); 
			msgThread.start();
			System.out.println("Hello envoyé");
	
		}catch (MessageCreationException e){
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	/**
	 * Receive method allows us to analyze the received packet
	 * and by that, we will be able to determine if the packet 
	 * is an instance of a hello, bye, message or file.
	 * This method is called by the listening thread when it 
	 * receives packet
	 *
	 * @param  mypacket consist of a DatagramPacket
	 */
	
	public void Receive(DatagramPacket myPacket) {
		// TODO Auto-generated method stub
		try {
            
            Message msgRcvd = MessagesFactory.getMessage(myPacket.getData());
            if (msgRcvd.getUsername().equals(cm.getLocalUser().getUsername())) {
                System.out.println("c ma propre message");
            }else if(msgRcvd instanceof ChatMessage){
           	 System.out.println("Message de " + MessagesFactory.getMessage(myPacket.getData()) + "@" +  MessagesFactory.getMessage(myPacket.getData()).getHostName() + ": ");
                System.out.println(((ChatMessage) MessagesFactory.getMessage(myPacket.getData())).getMsgToSend() + "\n");
                //on affiche le message recu par la personne 
                cc.performMsgRcvd(cm.getHashmapUsers().get(MessagesFactory.getMessage(myPacket.getData()).getUsername()) , ((ChatMessage) MessagesFactory.getMessage(myPacket.getData())).getMsgToSend() );
               }	
            
            if (msgRcvd instanceof Hello){
                System.out.println("je recois	 un message de type " + MessagesFactory.getMessage(myPacket.getData()).getClass());
                System.out.println("Il vient de " + MessagesFactory.getMessage(myPacket.getData()).getUsername() + "@" + MessagesFactory.getMessage(myPacket.getData()).getHostName());
                //on rajoute la personne dans la liste
                if(((Hello)msgRcvd ).getWaitAck()==true){
                	sendHello(cm.getLocalUser().getUsername(), false);
                    cc.performHelloRcvd(MessagesFactory.getMessage(myPacket.getData()).getUsername(), MessagesFactory.getMessage(myPacket.getData()).getHostName());	
                    User user=new User(MessagesFactory.getMessage(myPacket.getData()).getUsername(), MessagesFactory.getMessage(myPacket.getData()).getHostName());
              System.out.println("c'est ici qu'on doit voir si le hello est false");
                }
                if (((Hello)msgRcvd ).getWaitAck()==false){
                	System.out.println("on recu un hello avec un ack faux de "+MessagesFactory.getMessage(myPacket.getData()).getUsername()+"");	
   
                	cc.performHelloRcvd(MessagesFactory.getMessage(myPacket.getData()).getUsername(), MessagesFactory.getMessage(myPacket.getData()).getHostName());
                }
            }
                if ( msgRcvd instanceof Bye){
                    cc.performByeRcvd(cm.getHashmapUsers().get(msgRcvd.getUsername())); 
                }
            	
		} catch (MessageCreationException e) {
            e.printStackTrace();
		}
		
	}


	/**
	 * sendMsg allows us to send a message to a specific user 
	 * we start a sending thread and then we send the message
	 * to user using his ip adress
	 *
	 * @param  DestUser the name of the user we want to send him the message
	 * @param  msgToSend this the message that we want to send to the remote user
	 */
	
	public void sendMsg(String DestUser, String msgToSend) {
		// TODO Auto-generated method stub
		try{
	
			String hostname = cm.getHashmapUsers().get(DestUser).getHostname();
		     System.out.println("Chat message envoyé");
		     System.out.println("le message envoyé est "+msgToSend+" a destination de "+hostname);
			ThreadSendMsg msgThread=new ThreadSendMsg(MessagesFactory.getChatMessage(cm.getLocalUser().getUsername(), msgToSend), InetAddress.getByName(hostname), DEST_PORT); 
			msgThread.start();
		}catch (MessageCreationException e){
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	

	/**
	 *sendBye send a bye instance of a message in broadcast mode to everyone
	 *to notify them that the local user is getting disconnected
	 *
	 */
	public void sendBye() {
		// TODO Auto-generated method stub
		
		try {
			 ThreadSendMsg threadSendMsg;
			threadSendMsg = new ThreadSendMsg(MessagesFactory.getBye(cm.getLocalUser().getUsername()) ,InetAddress.getByName(BROADCAST), DEST_PORT);
			threadSendMsg.start();
	         cc.performByeSent();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MessageCreationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 *
	 *update is a method of the observer pattern that allows us 
	 *to send a hello or a bye message when the user changes his 
	 *connection state
	 *
	 * @param  arg0 is the observed class 
	 * @param  arg is the object 
	 */

	public void update(Observable arg0, Object arg) {
		// TODO Auto-generated method stub
		 if (arg == Val.CONNEXIONSTATE){
             if (arg instanceof User) {
                     if ((User)arg == cm.getLocalUser()) {
                             sendHello(BROADCAST, true);
                     }
                     else {
                             sendHello (((User)arg).getUsername(), false);
                     }
             }
     }

		

		
	}




}